package com.hongawen.unsafe;

import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;

/**
 * @author hongawen
 * @version 1.0.0
 * @date 2021年09月01日 09:10
 */
public class UnsafeDrawMoney {

    public static void main(String[] args) {
        Account account = new Account("家里老本", 100);
        ATM atm1 = new ATM(account, 50);
        ATM atm2 = new ATM(account, 100);
        new Thread(atm1, "洪圣文").start();
        new Thread(atm2, "王雪琴").start();
    }
}


/**
 * 账户
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
class Account {

    //账户名称
    public String name;

    //账户余额
    public volatile int money;

}

/**
 * 银行
 */
class ATM implements Runnable {

    //账户
    private Account account;

    //要取的钱
    private int drawMoney;

    int nowMoney;

    public ATM(Account account, int drawMoney) {
        this.account = account;
        this.drawMoney = drawMoney;
    }

    /**
     * 执行取钱操作
     */
    @SneakyThrows
    @Override
    public void run() {
        System.out.println(StrUtil.format("{}账户余额{},{}用户准备取{}万", account.getName(), account.getMoney(), Thread.currentThread().getName(), drawMoney));
        //不安全的如下：
//        if (account.getMoney() - drawMoney < 0) {
//            System.out.println(Thread.currentThread().getName() + "账户余额不足以取" + drawMoney + "万");
//            return;
//        }
//        Thread.sleep(1000);
//        nowMoney = nowMoney + drawMoney;
//        Thread.sleep(1000);
//        account.money = account.money - drawMoney;
//        System.out.println(StrUtil.format("银行支出{}", nowMoney));
//        System.out.println(StrUtil.format("{}账户余额{},{}用户手里有{}万", account.getName(), account.getMoney(), Thread.currentThread().getName(), drawMoney));

        //安全操作，将accout作为synchronized的锁对象
        synchronized (account){
            if (account.getMoney() - drawMoney < 0) {
                System.out.println(Thread.currentThread().getName() + "账户余额不足以取" + drawMoney + "万");
                return;
            }
            Thread.sleep(1000);
            nowMoney = nowMoney + drawMoney;
            Thread.sleep(1000);
            account.money = account.money - drawMoney;
            System.out.println(StrUtil.format("银行支出{}", nowMoney));
            System.out.println(StrUtil.format("{}账户余额{},{}用户手里有{}万", account.getName(), account.getMoney(), Thread.currentThread().getName(), drawMoney));
        }
    }
}







